import 'package:analyzer/dart/ast/ast.dart';
import 'package:dart_bean/cass_info.dart';
import 'package:dart_bean/util/ext.dart';
import 'package:dart_bean/util/field_feature.dart';
import 'package:dart_bean/field_info.dart';

class JsonFieldFeature extends FieldFeature {
  static final annotationClassName = 'JSONField';

  @override
  String? getJsonName(FieldInfo field) {
    return _getJsonField(field).name;
  }

  @override
  bool? isIgnoreDeserialize(FieldInfo field) {
    return _isIgnore(field, true);
  }

  @override
  bool? isIgnoreSerialize(FieldInfo field) {
    return _isIgnore(field, false);
  }

  bool _isIgnore(FieldInfo field, bool deserialize) {
    final jf = _getJsonField(field);
    return jf.ignore == true || (deserialize && jf.deserialize == false) || (!deserialize && jf.serialize == false);
  }

  JsonField _getJsonField(FieldInfo field) {
    var jsonField = field.buffers[JsonField] as JsonField?;
    if (jsonField != null) return jsonField;

    final Map<String, dynamic> map = {};
    final jf = field.self.metadata.firstOrNull((e) => e.name.name == annotationClassName);
    if (jf != null) {
      final args = jf.arguments?.arguments;
      if (args != null) {
        for (var e in args) {
          final ne = e as NamedExpression;
          final name = ne.name.label.name;
          final v = ne.expression;
          if (v is StringLiteral) {
            map[name] = v.stringValue;
          } else if (v is BooleanLiteral) {
            map[name] = v.value;
          }
        }
      }
    }
    jsonField = JsonField(
      name: map['name'],
      ignore: map['ignore'],
      serialize: map['serialize'],
      deserialize: map['deserialize'],
    );
    field.buffers[JsonField] = jsonField;
    return jsonField;
  }

  static bool hasClassUsingJsonField(CassInfo c) {
    return c.fields.any((e) {
      final jf = e.buffers[JsonField] as JsonField;
      return jf.isNotEmpty;
    });
  }
}

class JsonField {
  final bool isNotEmpty;

  //Specify the parse field name
  final String? name;

  //Whether to participate in toJson
  final bool? serialize;

  //Whether to participate in fromMap
  final bool? deserialize;

  final bool? ignore;

  const JsonField({this.name, this.ignore, this.serialize, this.deserialize}) : isNotEmpty = name != null || ignore != null || serialize != null || deserialize != null;
}
